package com.mofum.msdom.excel.writer.impl;

import com.mofum.msdom.excel.annotation.parser.impl.ATColumnParser;
import com.mofum.msdom.excel.annotation.parser.impl.ATWorkbookParser;
import com.mofum.msdom.excel.collections.FieldExcelConvertMap;
import com.mofum.msdom.excel.collections.FieldMPColumnMap;
import com.mofum.msdom.excel.collections.IntegerStringMap;
import com.mofum.msdom.excel.collections.type.SheetContext;
import com.mofum.msdom.excel.metadata.MPColumn;
import com.mofum.msdom.excel.metadata.MPSheet;
import com.mofum.msdom.excel.metadata.MPWorkbook;
import com.mofum.msdom.excel.reader.impl.AnnotationExcelReaderImpl;
import com.mofum.msdom.excel.utils.MetadataConvertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * 多类型注解Excel解析器
 *
 * @author 1615690513@qq.com
 * @since 2018/11/26 0026 14:15
 */
public class MultiAnnotationExcelWriterImpl<Data> extends MultiTypeExcelWriterImpl<Data> {

    public static Logger logger = LoggerFactory.getLogger(MultiAnnotationExcelWriterImpl.class);

    @Override
    public MultiAnnotationExcelWriterImpl start(OutputStream output) {
        if (getPrimaryClass() == null) {
            throw new RuntimeException("Output primary type must be set!");
        }
        this.setOutputStream(output);
        open();
        return this;
    }

    @Override
    public MultiAnnotationExcelWriterImpl dataType(Class<Data> dataClass) {

        this.setPrimaryClass(dataClass);

        MPSheet[] sheets = parseSheets(getPrimaryClass(), true);

        if (sheets.length == 0) {

            sheets = new MPSheet[]{MPSheet.DEFAUT_SHEET};

        }

        SheetContext primarySheetContext = new SheetContext();

        primarySheetContext.setDataClass(getPrimaryClass());

        primarySheetContext.setMpSheet(sheets[0]);

        setPrimarySheetContext(primarySheetContext, getPrimaryClass());

        try {

            createSheetContext(primarySheetContext, getPrimaryClass());

            addSheets(sheets);

        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }

        return this;
    }

    /**
     * 解析
     *
     * @param dataClass
     * @param setCacheFlag
     * @param <T>
     */
    private <T> MPSheet[] parseSheets(Class<T> dataClass, boolean setCacheFlag) {
        ATWorkbookParser atWorkbookParser = new ATWorkbookParser();

        Map<Type, MPWorkbook> maps = null;

        try {
            maps = atWorkbookParser.parseType(dataClass);

        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }

        if (maps.keySet().size() == 0) {
            throw new RuntimeException("No  annotations were detected in this class (" + dataClass.toString() + ") !");
        }

        MPWorkbook mpWorkbook = maps.get(dataClass);

        MPSheet[] sheets = mpWorkbook.getSheets();

        if (setCacheFlag) {
            setCache(mpWorkbook.getCache());
        }
        return sheets;
    }

    @Override
    public MultiAnnotationExcelWriterImpl addSheets(Class<?>... classes) {
        if (classes == null) {
            return null;
        }

        for (Class<?> clazz : classes) {
            addSheet(clazz);

        }
        return this;
    }

    @Override
    public MultiAnnotationExcelWriterImpl addSheet(Class<?> dataClass) {
        MPSheet[] sheets = parseSheets(dataClass, false);

        for (MPSheet mpSheet : sheets) {
            SheetContext sheetContext = new SheetContext();

            sheetContext.setMpSheet(mpSheet);

            createSheetContext(sheetContext, dataClass);

            addSheetContext(sheetContext);
        }
        return this;
    }

    /**
     * 创建工作表正文
     *
     * @param sheetContext 工作表正文
     * @param dataClass    数据类型
     */
    private <T> void createSheetContext(SheetContext sheetContext, Class<T> dataClass) {
        try {
            //创建解析器
            ATColumnParser<T> columnParser = new ATColumnParser<>();

            //解析出列
            Map<Field, MPColumn> columnMap = columnParser.parseField(dataClass.newInstance());

            FieldMPColumnMap mpColumnMap = new FieldMPColumnMap();

            for (Field field : columnMap.keySet()) {
                mpColumnMap.put(field, columnMap.get(field));
            }
            sheetContext.setColumnMap(mpColumnMap);


            //转换成索引Map
            IntegerStringMap indexMap = MetadataConvertUtils.columnMapToIndexMap(columnMap);

            //设置索引Map
            sheetContext.setColumnIndexMap(indexMap);

            FieldExcelConvertMap excelConverterMap = MetadataConvertUtils.columnMapToExcelConverterMap(columnMap, getExcelConfig().getExcelConverter());

            sheetContext.setExcelConvertMap(excelConverterMap);

        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }

    }


}
